package ywh.utils
{
	
	/**
	 * XML工具类，静态方法类;
	 * @author 杨伟华
	 * @version 2.0
	 * 
	 * Created 1/15/2013 11:32 PM
	 */
	
	public class XMLUtils
	{
		public static var TRUE_VALUE:String = "true";
		
		/**
		 * 获得指定节点的弟节点
		 * @param	item
		 * @return 弟节点
		 */
		public static function getNextSpiling(item:XML):XML
		{
			var parent:XML = item.parent();
			var targetNode:XML;
			if (parent && item.childIndex() < parent.*.length() - 1)
			{
				targetNode = parent.*[item.childIndex() + 1];
			}
			else
			{
				targetNode = null;
			}
			return targetNode;
		}
		
		/**
		 * 获得指定节点的兄节点
		 * @param	item
		 * @return 兄节点
		 */
		public static function getPrevSpiling(item:XML):XML
		{
			var parent:XML = item.parent();
			var targetNode:XML;
			if (parent && item.childIndex() > 0)
			{
				targetNode = parent.*[item.childIndex() - 1];
			}
			else
			{
				targetNode = null;
			}
			return targetNode;
		}
		
		/**
		 * 返回一个XML的根节点;
		 * @param	item
		 * @return
		 */
		public static function getRoot(item:XML):XML
		{
			if (item)
			{
				while (item.parent())
				{
					item = item.parent();
				}
			}
			return item;
		}
		
		/**
		 * 删除节点
		 * 【注意】delete操作符只能从上下文环境中删除属性，不能删除局部变量；
		 * 因此只能在父轴上删除XML;即便当前有变量直接引用了要被删除的XML节点;不一定非要在跟轴删除;
		 * @param	targetNode	准备删除的XML实例,可以是属性,可以是元素,可以是文本,但一定是XML而不是XMLList;
		 */
		public static function deleteXML(targetNode:XML):void
		{
			if (targetNode && targetNode.parent())
			{
				switch (targetNode.nodeKind())
				{
					case "element": 
						delete targetNode.parent()[targetNode.name()];
						break;
					case "text": 
						delete(targetNode.parent() as XMLList).child("*")[targetNode.childIndex()];
						break;
					case "attribute": 
						delete targetNode.parent()["@" + targetNode.name()];
						// delete targetNode.parent().attribute(targetNode.name())[0];//和上述语句功能一致
						break;
				}
			}
		}
		
		/**
		 * 在XMLList中寻找等于(或包含的属性)指定值的项组成的XMLList，并返回该XMLList；
		 * @param xl 标地XMLList;
		 * @param proValue Object,指定的值;
		 * @param pro String,指定的项目,如果此项为undefined,则直接查询元素本身的值;	,可以是子元素，也可以是@属性，如果是@属性，需要注明@；
		 */
		public static function getNodesByValue(xl:XMLList, propertyValue:String, propertyName:String = null):XMLList
		{
			var result:XMLList = null;
			if (propertyName != null)
			{
				// 以下写法兼容@属性和子元素;
				result = xl.(hasOwnProperty(propertyName) && valueOf()[propertyName] == propertyValue);
			}
			else
			{
				result = xl.(text() == propertyValue);
			}
			return result;
		}
		
		/**
		 * 寻找临界值的编号，二分法
		 * 这个方法应该比内置方法快，
		 * @param	targetXMLList 目标XMLList对象;
		 * @param	value 目标数值
		 * @param	toMin 如果前值有多个相等，如何处理;1，向左查找；0；不动;-1;向右查找;
		 * @param	prop 搜索属性名，
		 * @param	startIndex 起始序号
		 * @param	endIndex 结束序号
		 * @return
		 */
		public static function getNodeIndexBySort(targetXMLList:XMLList, value:Number, toMin:int, prop:String = null, startIndex:int = -1, endIndex:int = -1):int
		{
			if (!targetXMLList) {
				return -1;
				}
			if (endIndex < 0)
			{
				if (prop)
				{
					targetXMLList = targetXMLList.(hasOwnProperty(prop));
				}
				endIndex = targetXMLList.length() - 1;
			}
			var tempIndex:int;
			var tempValue:Number;
			var middleIndex:uint = Math.floor((startIndex + endIndex + 1) / 2);
			var middleNode:XML = targetXMLList[middleIndex];
			var middleValue:Number = (prop == null)? Number(middleNode.text()):Number(middleNode[prop]);
			if (startIndex == endIndex)
			{
				return targetXMLList[startIndex];
			}
			
			if (middleValue == value)
			{
				tempIndex= middleIndex;
			}else {
				if (middleValue > value)
				{
					endIndex = middleIndex - 1;
				}
				else
				{
					startIndex = middleIndex;
				}
				if (endIndex == startIndex)
				{
					tempIndex = startIndex;
				}
				else
				{
					return arguments.callee(targetXMLList, value, toMin, prop, startIndex, endIndex);
				}
			}
			if (toMin != 0)
			{
				var listLength:uint = targetXMLList.length();
				var pv:Number;
				tempValue = (prop == null)? Number(targetXMLList[tempIndex].text()):Number(targetXMLList[tempIndex][prop]);
				for (tempIndex += toMin; tempIndex > 0 && tempIndex < listLength; tempIndex += toMin)
				{
					pv = (prop == null)? Number(targetXMLList[tempIndex].text()):Number(targetXMLList[tempIndex][prop]);
					if (tempValue != pv)
					{
						tempIndex -=  toMin;
						break;
					}
				}
			}
			return tempIndex;
		}
		
		/**
		 * 寻找临界值的编号,此方法用了XML内置的方法寻找;
		 * @param	targetXMLList	标地XMLList
		 * @param	propertyValue	目标属性值
		 * @param	propertyName	目标属性,可以是子元素，也可以是@属性，如果是@属性，需要注明@；
		 * @return	返回符合小于等于目标值的元素的个数，也就是返回值的ID对应的那个元素的属性值事实上就是已经大于目标属性值。
		 */
		public static function getNodeIndexBySort2(targetXMLList:XMLList, propertyValue:Number, propertyName:String = null):XML
		{
			var index:int;
			if (propertyName != null)
			{
				index = targetXMLList.(hasOwnProperty(propertyName) && Number(valueOf()[propertyName]) < propertyValue).length() - 1;
			}
			else
			{
				index = targetXMLList.(Number(valueOf().text()) <= propertyValue).length() - 1;
			}
			return (index > -1) ? targetXMLList[index] : null;
		}
		
		/**
		 * 获得下一个具有某属性（含元素及属性）的节点
		 * @param	item
		 * @param	propertyName	属性名，如果如果为属性则应该以“@”开始;
		 * @param	lockLevel	是否仅在当前级别寻找;
		 * @return	后一个具有某属性的节点
		 */
		public static function findNextNodeWithProperty(item:XML, propertyName:String, lockLevel:Boolean = false):XML
		{
			var targetXML:XML;
			var nextSpiling:XML;
			var parentNode:XML;
			if (lockLevel)
			{
				item = getNextSpiling(item);
			}
			else if (item.hasComplexContent())
			{
				item = item.*[0];
			}
			else
			{
				item = getNextSpiling(item);
				if (!item)
				{
					parentNode = item.parent();
					while (parentNode)
					{
						nextSpiling = getNextSpiling(parentNode);
						if (nextSpiling)
						{
							item = nextSpiling;
							break;
						}
						parentNode = parentNode.parent();
					}
				}
			}
			
			targetXML = item ? (item.hasOwnProperty(propertyName) ? item : findNextNodeWithProperty(item, propertyName, lockLevel)) : null;
			return targetXML;
		}
		
		/**
		 * 获得前一个具有某属性（含元素及属性）的节点
		 * @param	item
		 * @param	propertyName	属性名，如果如果为属性则应该以“@”开始;
		 * @param	lockLevel 是否锁定当前层级寻找
		 * @return	前一个具有某属性的节点
		 */
		public static function findPrevNodeWithProperty(item:XML, propertyName:String, lockLevel:Boolean = false):XML
		{
			var targetXML:XML;
			var prevSpiling:XML = getPrevSpiling(item);
			var parentNode:XML;
			if (lockLevel && prevSpiling)
			{
				item = prevSpiling;
			}
			else if (prevSpiling)
			{
				item = prevSpiling;
				while (item.hasComplexContent())
				{
					item = item.*[item.*.length() - 1];
				}
			}
			else
			{
				parentNode = item.parent();
				item = parentNode ? parentNode : null;
			}
			targetXML = item ? (item.hasOwnProperty(propertyName) ? item : findPrevNodeWithProperty(item, propertyName, lockLevel)) : null;
			return targetXML;
		}
		
		/**
		 * 获取XML对象的属性值;
		 * @param	xml
		 * @param	infoName
		 * @return
		 */
		public static function getPropString(xml:XML,infoName:String):String
		{
			return (xml && xml.hasOwnProperty(infoName)) ? (xml[infoName][0] as XML).toString() : null;
		}
		
		/**
		 * 获取XML对象的属性值,数字型;
		 * @param	xml
		 * @param	infoName
		 * @return
		 */
		public static function getPropNumber(xml:XML,infoName:String):Number
		{
			var value:String = getPropString(xml,infoName);
			return value ? Number(value) : NaN;
		}
		
		/**
		 * 获取XML对象的属性值，布尔类型;
		 * @param	xml
		 * @param	infoName
		 * @return
		 */
		public static function getPropBoolean(xml:XML,infoName:String):Boolean
		{
			return getPropString( xml,infoName) == TRUE_VALUE;
		}
		
		/**
		 * Creates an object with properties extracted from XML attributes
		 * 将单个XML节点转换成对象;通过XML属性构建Object;
		 * @param node
		 */
		public static function createObjectFromXMLAttributes(node:XML):Object
		{
			//create an object and give it props from this nodes attributes
			var obj:Object = {};
			var attrs:XMLList = node.attributes();
			for each (var attr:XML in attrs)
			{
				obj[attr.localName()] = attr.toString();
			}
			return obj;
		}
		
		/**
		 * Creates a hierarchical array from XML or an XMLList
		 * 通过XML或者XMLList构建一个分层级数组;
		 * 每个节点都是一个对象;
		 * 当前节点的子节点构成数组，存储在代表当前节点的对象的data属性中;
		 * @param xml An XML or XMLList object
		 */
		public static function createArrayFromXML(xml:Object):Array
		{
			var nodes:XMLList;
			var ar:Array = [];
			if (xml is XML)
			{
				nodes = XML(xml).children();
			}
			else if (xml is XMLList)
			{
				nodes = xml as XMLList;
			}
			else
			{
				throw new TypeError("Error: Type Coercion failed: cannot convert " + xml + " to XML or XMLList.");
				return null;
			}
			//loop through top level nodes
			for each (var node:XML in nodes)
			{
				var obj:Object = createObjectFromXMLAttributes(node);
				//loop through any nodes in this node
				var propNodes:XMLList = node.*;
				var data:Array = createArrayFromXML(propNodes);
				if (data.length)
				{
					obj.data = data;
				}
				ar.push(obj);
			}
			return ar;
		}
		
		/**
		 * 将XML构建成一维数组;每个子项是一个对象，其中含有一个属性名为structArray，其中包含XML对象获取路径;
		 * @param	xml
		 * @return
		 */
		public static function createLinerArrayFromXML(xml:Object, resultArray:Array = null, structArray:Array = null):Array
		{
			resultArray = resultArray ? resultArray : [];
			structArray = structArray ? structArray : [];
			var nodes:XMLList;
			var length:uint;
			var node:XML;
			if (xml is XML)
			{
				nodes = XML(xml).children();
				length = nodes.length();
			}
			else
			{
				throw new TypeError("Error: Type Coercion failed: cannot convert " + xml + " to XML or XMLList.");
				return null;
			}
			for (var i:uint = 0; i < length; i++)
			{
				node = nodes[i];
				var obj:Object = createObjectFromXMLAttributes(node);
				obj.structArray = structArray.concat(i);
				resultArray.push(obj);
				if (!node.hasSimpleContent())
				{
					createLinerArrayFromXML(node, resultArray, obj.structArray);
				}
			}
			return resultArray;
		}
	}
}
